/*
 * 2021/12/27	11:17	yin	
 */

/*
 * bad password			P229
 */
	004011c0	push ecx
				push esi
				mov esi, ss:[esp+c]
				push 0									# origin = FILE_BEGIN
				push 0									# pOffsetHi = NULL
				push 0									# OffsetLo = 0
				push esi								# hFile
				call ds:[<&KERNEL32.SetFilePointer>]
				push 0									# pOverlapped = NULL
				lea eax, ss:[esp+8]
				push eax								# pBytesRead
				push 28									# BytesToRead = 28 (40)
				push cryptex.00406058					# Buffer = cryptex.00406058			
				push esi								# hFile	
				call ds:ReadFile						# ReadFile			
				test eax, eax									
				jnz 0040ffef
				pop esi
				pop ecx
				retn
				cmp dword ptr ds:[406058], 70597243		# 70597243 是 CrYp字符的十六进制
				jnz short 0040123c
				cmp dword ptr ds:[40605c], 39586554		# 39586554 TeX9 的十六进制
				jnz short cryptex.0040123c
				push edi
				mov ecx, 4
				mov edi, cryptex.00405038				# 第一个串
				mov esi, cryptex.00406070				# 第二个数据串
				xor edx, edx
				repe cmps dword ptr es:[edi], dword ptr ds:[esi]	# 循环比较两个dword数据串,分别由edi esi 指向，
																	# 并在每次迭代之后将两个寄存器都加1。
																	# 迭代交数由 ecx 的值确定。
																	# ecx=4,意味着该指令将比较四个dwrod(共16个字节)
				pop edi
				je short cryptex.00401234							# 相等
				push cryptex.00403170		; format = "error:Invalid password. Unable to process file.""
				call ds:[<&MSVCR71.printf>]	; printf
				add esp, 4
				push 1						; status = 1
				call ds:[<&msvcr71.exit>]	; exit
				mov eax, 1
				pop esi
				pop ecx
				retn
	0040123c	push cryptex.0040313c		; format = "error:Invalid cryptex8 signature in file header!"
				call ds:[<&MSVCR71.printf>]	; printf
				add esp, 4
				push 1						; status = 1
				call ds:[<&MSVCR71.exit>]	; exit

	此函数执行的就是对该.crx文件的头部确认。在开始的时候将文件指针(file pointer)指向该.crx文件的0处
	(使用 SetFilePointer API函数),然后使用 ReadFile 读取了.crx文件中的前0x28个字节。读出来头部数据被存放在 00406058 处的
	数据结构中。
	很容易看出 00406058 这个地址实际上是某个全局变量(绝对不是堆地址或者堆栈地址),因为它离代码地址很近。

	mov ecx, 4 接下来的代码才是要找的(因为这段代码决定了该函数返回在1还是打印错误口令消息)。
	这段代码比较了内存中的两个16字节的串，并在不相等的情况下打印错误消息。
	第一个串是从 00405038 处开始的，这个串是另一个全局变量，它的内容在这里还不知道。
	第二个串从 00406070 处开始，它是前边看到的从 00406058 处开始的头部全局变量的一部分。
	显然， ReadFile 将 0x28 个字节读入了 00406058 这个地址， 00406070 这个地址超过了 00406058 共 0x18 个字节，
	因此，这个缓冲区中还留有 0x10 个字节。

	真正的比较是用 "repe cmps"完成的，该指令循环比较了两个DWORD数年串，两个数据串分别由EDI和ESI指向，并在第次迭代之后将两个
	寄存器都加 1 。迭代次数由 ECX 的值确定。


/*
 * 口令变换算法		P233
 */
	要找到将明文口令转换为16字节串的算法在程序中的位置，最简单的方法就是在存放当前键入口令的全局变量上设置一个内存断点。

	00402280 mov ecx, ds:[405048]
			 sub esp, 8
			 lea eax, ss:[esp]
			 push eax
			 push 0
			 push 0
			 push 8003
			 push ecx
			 call ds:[<&advapi32.CryptCreateHash>]
			 test eax, eax
			 je short cryptex.004022c2
			 mov edx, ss:[esp+c]
			 mov eax, ss:[esp]
			 push 0
			 push 14
			 push edx
			 push eax
			 call CryptHashData
			 test eax, eax
			 mov ecx, ss:[esp]
			 jnz short cryptex.004022c8
			 push ecx
			 call CryptDestroyHash
			 xor eax, eax
			 add esp, 8
			 retn
			 mov eax, ss:[esp+10]
			 push esi
			 push 0
			 lea edx, ss:[esp+c]
			 push edx
			 push eax
			 push 2
			 push ecx
			 mov dword ptr ss:[esp+1c], 10
			 call CryptGetHashParam
			 mov edx, ss:[esp+4]
			 push edx
			 mov esi, eax
			 call CryptDestroyHash
			 mov eax, esi
			 pop esi
			 add esp, 8
			 retn
	
		MD5 (Message-digest) 使用非常广泛的密码 hashing 算法，它可以用一个可变长度的消息生成一个很长的(128 BIT) hash 或校验
		和 (checksun)。这个hash在以后可以用来唯一确定某一条特定的消息。

/*
 * 对口令hash处理		P235
 */
	00402300 sub esp, 24
			 mov eax, ds:[405020]
			 push edi
			 mov edi, ss:[esp+2c]
			 mov ss:[esp+24], eax
			 lea eax, ss:[esp+4]
			 push eax
			 push 0
			 push 0
			 push 8004				# 作为算法的标识符（ID）
			 push edi
			 call CryptCreateHash
			 test eax, eax
			 je cryptex.004023ca
			 mov edx, ss:[esp+30]
			 mov eax, edx
			 push esi
			 lea esi, ds:[eax+1]
			 mov cl, ds:[eax]
			 add eax, 1
			 test cl, cl
			 jnz short cryptex.00402338
			 mov ecx, ss:[esp+8]
			 push 0
			 sub eax, esi
			 push eax
			 push edx
			 push ecx
			 call CryptHashData
			 test eax, eax
			 pop esi
			 je short cryptex.004023bf
			 xor eax, eax
			 mov ss:[esp+11], eax
			 mov ss:[esp+15], eax
			 mov ss:[esp+19], eax
			 mov ss:[esp+1D], eax
			 mov ss:[esp+21], ax
			 lea ecx, ss:[esp+c]
			 lea edx, ss:[esp+10]
			 mov ss:[esp+23], al
			 mov byte ptr ss:[esp+10], 0
			 mov dword ptr ss:[esp+c], 14
			 push eax
			 mov eax, ss:[esp+8]
			 push ecx
			 push edx
			 push 2
			 push eax
			 call CryptGetHashParam
			 test eax, eax
			 jnz short 004023a9
			 push cryptex.00403504	; format = "unable to botain MD5 hash value for file."
			 call printf
			 add esp, 4
			 lea ecx, ss:[esp+10]
			 push cryptex.00405038
			 push ecx
			 call cryptex.00402280
			 add esp, 8
			 test eax, eax
			 jnz short cryptex.004023da
			 mov edx, ss:[esp+4]
			 push edx
			 call CryptDestroyHash
			 xor eax, eax
			 pop edi
			 mov ecx, ss:[esp+20]
			 call cryptex.004027c9
			 add esp, 24
			 retn
			 mov ecx, ss:[esp+4]
			 lea eax, ss:[esp+8]
			 push eax
			 push 0
			 push ecx
			 push 6603
			 push edi
			 mov dword ptr ss:[esp+1c], 0
			 call CryptDeriverKey
			 mov edx, ss:[esp+4]
			 push edx
			 call CryptDestroyHash
			 mov ecx, ss:[esp+24]
			 mov eax, ss:[esp+8]
			 pop edi
			 call cryptex.004027c9
			 add esp, 24
			 retn

	这个函数一开始创建了一个hash对象并对一些数据进行了hash处理。
		
	00402335 lea esi, ds:[eax+1]
	00402338 mov cl, ds:[eax]
	0040233A add eax, 1
	0040233D test cl, cl
	0040233F jnz short cryptex.00402338

	这个循环它从口令字符串中逐个读取字符，每读一个就检查它是否为零。
	如果不为0就循环到下一个字符。当循环结束时，EAX指向的是该字符串的结束标志字符NULL，ESI指向该字符串的第二个字符。
	下面这条指令生成了最终的结果。
	00402347 sub eax, esi
	这里用指向NULL结束符的指针减去指向第二个字符的指针。
	减法运算的结果就是这个字符串的有效长度，当然不包括NULL结束符(因为ESI存放的是第二个字符的地址，而不是第一个字符的地址).
	这段代码实际上相当于C的strlen。		




































